﻿using MiniFox.Components.Configurations;
using System.Reflection;

namespace MiniFox.Components
{
    /// <summary>
    /// 
    /// </summary>
    public static class ComponentFactory
    {
        static readonly object __containerLocker;
        static readonly object __assemblyLocker;
        static readonly object __appStartTypesLocker;
        static readonly object __contractTypesLocker;
        static readonly object __componentTypesLocker;
        static bool _initialized = false;
        static Type[] _appStartTypes;
        static Type[] _contractTypes;
        static Type[] _componentTypes;
        static Assembly[] _assemblies;
        static IComponentContainerConfiguration _componentContainerConfiguration;
        static ComponentContainer _Container;
        /// <summary>
        /// 
        /// </summary>
        static ComponentFactory()
        {
            __containerLocker = new object();
            __assemblyLocker = new object();
            __appStartTypesLocker = new object();
            __contractTypesLocker = new object();
            __componentTypesLocker = new object();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="componentContainerConfiguration"></param>
        public static void Initialize(IComponentContainerConfiguration componentContainerConfiguration)
        {
            if (_initialized)
                return;
            _componentContainerConfiguration = componentContainerConfiguration;
            Container.Initialize();
            _initialized = true;
        }
        /// <summary>
        /// 
        /// </summary>
        internal static IComponentContainerConfiguration ComponentsConfiguration
        {
            get
            {
                return _componentContainerConfiguration;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public static ComponentContainer Container
        {
            get 
            {
                if (_Container == null)
                {
                    lock (__containerLocker)
                    {
                        if (_Container == null)
                        {
                            try
                            {
                                if (ComponentsConfiguration == null || string.IsNullOrEmpty(ComponentsConfiguration.ContainerType))
                                {
                                    _Container = new DefaultComponentContainer(ComponentsConfiguration);
                                }
                                else
                                {
                                    Type containerType = Type.GetType(ComponentsConfiguration.ContainerType, true, true);
                                    _Container = (ComponentContainer)Activator.CreateInstance(containerType, ComponentsConfiguration);
                                }
                            }
                            catch
                            {
                                _Container = new DefaultComponentContainer(ComponentsConfiguration);
                            }
                        }
                    }
                }
                return _Container; 
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="contractType"></param>
        public static void RegisterComponent(Type contractType)
        {
            Container.RegisterComponent(contractType);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TContract"></typeparam>
        public static void RegisterComponent<TContract>()
        {
            RegisterComponent(typeof(TContract));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TContract"></typeparam>
        /// <param name="args"></param>
        /// <returns></returns>
        public static TContract GetComponent<TContract>(params object[] args)
        {
            TContract component = Container.GetComponent<TContract>(args);
            return component;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TContract"></typeparam>
        /// <param name="componentName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static TContract GetComponentByName<TContract>(string componentName, params object[] args)
        {
            TContract component = Container.GetComponentByName<TContract>(componentName, args);
            return component;
        }
 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="contractType"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static object GetComponent(Type contractType, params object[] args)
        {
            return Container.GetComponent(contractType, args);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="contractType"></param>
        /// <param name="componentName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static object GetComponentByName(Type contractType, string componentName, params object[] args)
        {
            return Container.GetComponentByName(contractType, componentName, args);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="contractType"></param>
        /// <returns></returns>
        public static bool IsRegistered(Type contractType)
        {
            return Container.IsRegistered(contractType);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TContract"></typeparam>
        /// <returns></returns>
        public static bool IsRegistered<TContract>()
        {
            return Container.IsRegistered<TContract>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">特性类</typeparam>
        /// <param name="assembly"></param>
        /// <returns></returns>
        public static IEnumerable<Type> GetTypesByAttribute<T>(this Assembly assembly) where T : Attribute
        {
            return GetTypesByAttribute<T>(assembly, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">特性类</typeparam>
        /// <param name="assembly"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        public static IEnumerable<Type> GetTypesByAttribute<T>(this Assembly assembly, Func<Type, bool> func) where T : Attribute
        {
            try
            {
                var types = assembly.GetTypes();

                return types.Where(t => t.GetCustomAttribute<T>() != null && (func == null || func(t)));
            }
            catch
            {
                return null;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assembly"></param>
        /// <returns></returns>
        public static IEnumerable<Type> GetTypes<T>(this Assembly assembly)
        {
            return GetTypes<T>(assembly, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">关联的基类或接口类型</typeparam>
        /// <param name="assembly"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        public static IEnumerable<Type> GetTypes<T>(this Assembly assembly, Func<Type, bool> func)
        {
            try
            {
                var types = assembly.GetTypes();

                return types.Where(t => t.IsAssignableTo(typeof(T)) && (func == null || func(t)));
            }
            catch
            {
                return null;
            }
        }


        /// <summary>
        /// 
        /// </summary>
        internal static Assembly[] Assemblies
        {
            get
            {
                lock (__assemblyLocker)
                {
                    if (_assemblies == null || _assemblies.Length <= 0)
                    {
                        var binDirectory = AppDomain.CurrentDomain.BaseDirectory;
                        string[] allFiles = Directory.GetFiles(binDirectory, "*.dll", SearchOption.TopDirectoryOnly);
                        List<Assembly> assemblies = new List<Assembly>();
                        foreach (string file in allFiles)
                        {
                            try
                            {
                                var assembly = Assembly.LoadFrom(file);
                                assemblies.Add(assembly);
                            }
                            catch
                            {
                                continue;
                            }
                        }
                        _assemblies = assemblies.ToArray();
                    }
                }
                return _assemblies;
            }
        }
        internal static Type[] AppStartTypes
        {
            get
            {
                lock (__appStartTypesLocker)
                {
                    if (_appStartTypes == null || _appStartTypes.Length <= 0)
                    {
                        List<Type> list = new List<Type>();
                        foreach (var assembly in Assemblies)
                        {
                            var types = assembly.GetTypes<IApplicationRunner>(t =>
                            {
                                var valid = t.IsClass && !t.IsAbstract && !t.ContainsGenericParameters && !t.IsArray && !t.IsValueType && !t.IsEnum;

                                return valid;
                            });
                            if (types != null && types.Count() > 0)
                                list.AddRange(types);
                        }
                        _appStartTypes = list.OrderBy(t =>
                        {
                            var attr = t.GetCustomAttribute<StartOrdinalAttribute>();
                            if (attr == null)
                                return 0;
                            return attr.Ordinal;
                        }).ToArray();
                    }
                }
                return _appStartTypes;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        internal static Type[] ContractTypes
        {
            get
            {
                lock (__contractTypesLocker)
                {
                    if (_contractTypes == null || _contractTypes.Length <= 0)
                    {
                        List<Type> list = new List<Type>();
                        foreach (var assembly in Assemblies)
                        {
                            var types = assembly.GetTypesByAttribute<AutoRegisterAttribute>(t => (t.IsInterface || t.IsClass) && !t.IsSealed && !t.IsArray && !t.IsValueType && !t.IsEnum);
                            if (types != null && types.Count() > 0)
                                list.AddRange(types);
                        }
                        _contractTypes = list.ToArray();
                    }
                }
                return _contractTypes;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        internal static Type[] ComponentTypes
        {
            get
            {
                if (_componentTypes == null || _componentTypes.Length <= 0)
                {
                    lock (__componentTypesLocker)
                    {
                        if (_componentTypes == null || _componentTypes.Length <= 0)
                        {
                            List<Type> list = new List<Type>();
                            foreach (var assembly in Assemblies)
                            {
                                var types = assembly.GetTypesByAttribute<AutoResolveAttribute>(t => t.IsClass && !t.IsInterface && !t.IsAbstract && !t.IsArray && !t.IsValueType && !t.IsEnum);
                                if (types != null && types.Count() > 0)
                                    list.AddRange(types);
                            }
                            _componentTypes = list.ToArray();
                        }
                    }
                }
                return _componentTypes;
            }
        }

    }
}
